// 2021年，dudaodong@gmail.com保留所有权利。所有使用此源代码均需遵循MIT许可协议。
// 验证函数,从github.com/duke-git/lancet/v2复制
// 包 validator 实现了一些用于字符串的验证函数。
package 扩展类

import (
	"encoding/json"
	"fmt"
	"net"
	"net/mail"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"
)

var (
	alphaMatcher           *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
	letterRegexMatcher     *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
	numberRegexMatcher     *regexp.Regexp = regexp.MustCompile(`\d`)
	intStrMatcher          *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
	urlMatcher             *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
	dnsMatcher             *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)
	emailMatcher           *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
	chineseMobileMatcher   *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
	chineseIdMatcher       *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`)
	chineseMatcher         *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
	chinesePhoneMatcher    *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}|\d{4}-\d{8}`)
	creditCardMatcher      *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
	base64Matcher          *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
	base64URLMatcher       *regexp.Regexp = regexp.MustCompile(`^([A-Za-z0-9_-]{4})*([A-Za-z0-9_-]{2}(==)?|[A-Za-z0-9_-]{3}=?)?$`)
	binMatcher             *regexp.Regexp = regexp.MustCompile(`^(0b)?[01]+$`)
	hexMatcher             *regexp.Regexp = regexp.MustCompile(`^(#|0x|0X)?[0-9a-fA-F]+$`)
	visaMatcher            *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`)
	masterCardMatcher      *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`)
	americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`)
	unionPay               *regexp.Regexp = regexp.MustCompile("^62[0-5]\\d{13,16}$")
	chinaUnionPay          *regexp.Regexp = regexp.MustCompile(`^62[0-9]{14,17}$`)
)

var (
	// Identity card formula
	factor = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
	// ID verification bit
	verifyStr = [11]string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}
	// 身份证开始的年份
	birthStartYear = 1900
	// Province code
	provinceKv = map[string]struct{}{
		"11": {},
		"12": {},
		"13": {},
		"14": {},
		"15": {},
		"21": {},
		"22": {},
		"23": {},
		"31": {},
		"32": {},
		"33": {},
		"34": {},
		"35": {},
		"36": {},
		"37": {},
		"41": {},
		"42": {},
		"43": {},
		"44": {},
		"45": {},
		"46": {},
		"50": {},
		"51": {},
		"52": {},
		"53": {},
		"54": {},
		"61": {},
		"62": {},
		"63": {},
		"64": {},
		"65": {},
		// "71": {},
		// "81": {},
		// "82": {},
	}
)

// X是否全部字母 检查字符串是否只包含字母 (a-zA-Z)。
// 在线尝试: https://go.dev/play/p/7Q5sGOz2izQ
func X是否全部字母(文本 string) bool {
	return alphaMatcher.MatchString(文本)
}

// X是否全部大写字母 函数用于检查字符串是否全由大写字母A-Z组成。
// 示例：https://go.dev/play/p/ZHctgeK1n4Z
func X是否全部大写字母(文本 string) bool {
	for _, r := range 文本 {
		if !unicode.IsUpper(r) {
			return false
		}
	}
	return 文本 != ""
}

// X是否全部小写字母 函数用于检查字符串是否全由小写英文字母a-z组成。
// 示例：https://go.dev/play/p/GjqCnOfV6cM
func X是否全部小写字母(文本 string) bool {
	for _, r := range 文本 {
		if !unicode.IsLower(r) {
			return false
		}
	}
	return 文本 != ""
}

// X是否全部ASCII字符 检查字符串是否全都是 ASCII 字符。
// 在线尝试: https://go.dev/play/p/hfQNPLX0jNa
func X是否全部ASCII字符(文本 string) bool {
	for i := 0; i < len(文本); i++ {
		if 文本[i] > unicode.MaxASCII {
			return false
		}
	}
	return true
}

// X是否全部可打印字符 函数检查字符串是否全部由可打印字符组成。
// 示例：https://go.dev/play/p/Pe1FE2gdtTP
func X是否全部可打印字符(文本 string) bool {
	for _, r := range 文本 {
		if !unicode.IsPrint(r) {
			if r == '\n' || r == '\r' || r == '\t' || r == '`' {
				continue
			}
			return false
		}
	}
	return true
}

// X是否包含大写字母 函数检查字符串中是否至少包含一个大写字母A-Z。
// 示例：https://go.dev/play/p/CmWeBEk27-z
func X是否包含大写字母(文本 string) bool {
	for _, r := range 文本 {
		if unicode.IsUpper(r) && unicode.IsLetter(r) {
			return true
		}
	}
	return false
}

// X是否包含小写字母 检查字符串中是否至少包含一个小写字母 a-z。
// 在线测试: https://go.dev/play/p/Srqi1ItvnAA
func X是否包含小写字母(文本 string) bool {
	for _, r := range 文本 {
		if unicode.IsLower(r) && unicode.IsLetter(r) {
			return true
		}
	}
	return false
}

// X是否包含字母 函数检查字符串中是否至少包含一个字母。
// 示例：https://go.dev/play/p/lqFD04Yyewp
func X是否包含字母(文本 string) bool {
	return letterRegexMatcher.MatchString(文本)
}

// X是否包含数字 检查字符串是否至少包含一个数字。
func X是否包含数字(文本 string) bool {
	return numberRegexMatcher.MatchString(文本)
}

// X是否为JSON 检查字符串是否是有效的 JSON。
// 在线演示: https://go.dev/play/p/8Kip1Itjiil
func X是否为JSON(文本 string) bool {
	var js json.RawMessage
	return json.Unmarshal([]byte(文本), &js) == nil
}

// X是否为数值文本 函数检查字符串是否可以转换为数字。
// 示例：https://go.dev/play/p/LzaKocSV79u
func X是否为数值文本(文本 string) bool {
	return X是否为整数文本(文本) || X是否为小数文本(文本)
}

// IsFloatStr检查字符串是否可以转换为浮点数。
// 示例：https://go.dev/play/p/LOYwS_Oyl7U
func X是否为小数文本(文本 string) bool {
	_, e := strconv.ParseFloat(文本, 64)
	return e == nil
}

// X是否为整数文本 检查该字符串是否可以转换为整数。
// 在线演示: https://go.dev/play/p/jQRtFv-a0Rk
func X是否为整数文本(文本 string) bool {
	return intStrMatcher.MatchString(文本)
}

// X是否为IP 检查字符串是否为ip地址。
// 示例：https://go.dev/play/p/FgcplDvmxoD
func X是否为IP(文本 string) bool {
	ip := net.ParseIP(文本)
	return ip != nil
}

// IsIpV4用于检查字符串是否为ipv4地址。
// 示例：https://go.dev/play/p/zBGT99EjaIu
func X是否为IPV4(文本 string) bool {
	ip := net.ParseIP(文本)
	if ip == nil {
		return false
	}
	return ip.To4() != nil
}

// X是否为IPV6 用于检查字符串是否为ipv6地址。
// 在线测试: https://go.dev/play/p/AHA0r0AzIdC
func X是否为IPV6(文本 string) bool {
	ip := net.ParseIP(文本)
	if ip == nil {
		return false
	}
	return ip.To4() == nil && len(ip) == net.IPv6len
}

// X是否为端口 检查字符串是否为有效的网络端口。
// 示例：
func X是否为端口(文本 string) bool {
	if i, err := strconv.ParseInt(文本, 10, 64); err == nil && i > 0 && i < 65536 {
		return true
	}
	return false
}

// X是否为Url 检查字符串是否为URL。
// 示例：https://go.dev/play/p/pbJGa7F98Ka
func X是否为Url(文本 string) bool {
	if 文本 == "" || len(文本) >= 2083 || len(文本) <= 3 || strings.HasPrefix(文本, ".") {
		return false
	}
	u, err := url.Parse(文本)
	if err != nil {
		return false
	}
	if strings.HasPrefix(u.Host, ".") {
		return false
	}
	if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
		return false
	}

	return urlMatcher.MatchString(文本)
}

// X是否为Dns 检查该字符串是否为域名系统（DNS）。
// 试用: https://go.dev/play/p/jlYApVLLGTZ
func X是否为Dns(文本 string) bool {
	return dnsMatcher.MatchString(文本)
}

// X是否为邮箱 检查字符串是否为电子邮件地址。
// 示例：https://go.dev/play/p/Os9VaFlT33G
func X是否为邮箱(文本 string) bool {
	_, err := mail.ParseAddress(文本)
	return err == nil

	// 返回 emailMatcher 是否匹配 文本
}

// X是否为中国手机号 检查该字符串是否为中国手机号码。
// 在线测试: https://go.dev/play/p/GPYUlGTOqe3
func X是否为中国手机号(文本 string) bool {
	return chineseMobileMatcher.MatchString(文本)
}

// IsChineseIdNum用于检查字符串是否为中国身份证号。
// 示例：https://go.dev/play/p/d8EWhl2UGDF
func X是否为中国身份证号(文本 string) bool {
	// 所有字符都应该是数字，最后一位可以是x或X
	if !chineseIdMatcher.MatchString(文本) {
		return false
	}

	// 验证省份代码并根据GB/T2260标准完成所有省份代码的补充
	_, ok := provinceKv[文本[0:2]]
	if !ok {
		return false
	}

	// 验证生日，必须大于出生开始年份且小于当前年份
	birthStr := fmt.Sprintf("%s-%s-%s", 文本[6:10], 文本[10:12], 文本[12:14])
	birthday, err := time.Parse("2006-01-02", birthStr)
	if err != nil || birthday.After(time.Now()) || birthday.Year() < birthStartYear {
		return false
	}

	// Verification code
	sum := 0
	for i, c := range 文本[:17] {
		v, _ := strconv.Atoi(string(c))
		sum += v * factor[i]
	}

	return verifyStr[sum%11] == strings.ToUpper(文本[17:18])
}

// X是否包含汉字 检查字符串是否包含普通话汉字。
// 示例：https://go.dev/play/p/7DpU0uElYeM
func X是否包含汉字(文本 string) bool {
	return chineseMatcher.MatchString(文本)
}

// X是否为中国电话号码 检查该字符串是否为中国电话号码。
// 有效的中国电话号码形式为 xxx-xxxxxxxx 或 xxxx-xxxxxxx。
// 在线测试: https://go.dev/play/p/RUD_-7YZJ3I
func X是否为中国电话号码(文本 string) bool {
	return chinesePhoneMatcher.MatchString(文本)
}

// X是否为信用卡号 函数用于检查字符串是否为信用卡号。
// 示例：https://go.dev/play/p/sNwwL6B0-v4
func X是否为信用卡号(文本 string) bool {
	return creditCardMatcher.MatchString(文本)
}

// X是否为Base64 检查字符串是否为Base64编码的字符串。
// 示例：https://go.dev/play/p/sWHEySAt6hl
func X是否为Base64(文本 string) bool {
	return base64Matcher.MatchString(文本)
}

// X是否为空 检查字符串是否为空。
// 在线演示: https://go.dev/play/p/dpzgUjFnBCX
func X是否为空(文本 string) bool {
	return len(文本) == 0
}

// X是否匹配正则 函数检查字符串是否匹配正则表达式。
// 示例：https://go.dev/play/p/z_XeZo_litG
func X是否匹配正则(文本, 表达式 string) bool {
	reg := regexp.MustCompile(表达式)
	return reg.MatchString(文本)
}

// X是否为强密码 函数用于检查字符串是否为强密码。如果password的长度小于length参数，则返回false。
// 强密码定义：包含字母（大小写）、数字和特殊字符（!@#$%^&*()<>？）。
// 示例代码可以在这里查看：https://go.dev/play/p/QHdVcSQ3uDg
func X是否为强密码(密码 string, 长度 int) bool {
	if len(密码) < 长度 {
		return false
	}
	var num, lower, upper, special bool
	for _, r := range 密码 {
		switch {
		case unicode.IsDigit(r):
			num = true
		case unicode.IsUpper(r):
			upper = true
		case unicode.IsLower(r):
			lower = true
		case unicode.IsSymbol(r), unicode.IsPunct(r):
			special = true
		}
	}

	return num && lower && upper && special
}

// X是否为弱密码 检查该字符串是否为弱密码
// 弱密码定义：仅包含字母或仅包含数字或字母+数字的组合。
// 试玩: https://go.dev/play/p/wqakscZH5gH
func X是否为弱密码(密码 string) bool {
	var num, letter, special bool
	for _, r := range 密码 {
		switch {
		case unicode.IsDigit(r):
			num = true
		case unicode.IsLetter(r):
			letter = true
		case unicode.IsSymbol(r), unicode.IsPunct(r):
			special = true
		}
	}

	return (num || letter) && !special
}

// X是否为零值 检查值是否为零值。
// 示例：https://go.dev/play/p/UMrwaDCi_t4
func X是否为零值(value any) bool {
	if value == nil {
		return true
	}

	rv := reflect.ValueOf(value)
	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}

	if !rv.IsValid() {
		return true
	}

	switch rv.Kind() {
	case reflect.String:
		return rv.Len() == 0
	case reflect.Bool:
		return !rv.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return rv.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return rv.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return rv.Float() == 0
	case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice, reflect.Map:
		return rv.IsNil()
	}

	return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface())
}

// X是否为数值 检查值是否为数字（整数，浮点数）。
// 示例：https://go.dev/play/p/mdJHOAvtsvF
func X是否为数值(v any) bool {
	return X是否为整数(v) || X是否为小数(v)
}

// X是否为小数 检查值是否为浮点数（float32 或 float64）。
// 示例：https://go.dev/play/p/vsyG-sxr99_Z
func X是否为小数(v any) bool {
	switch v.(type) {
	case float32, float64:
		return true
	}
	return false
}

// X是否为整数 检查值是否为整数（int, unit）。
// 在线演示: https://go.dev/play/p/eFoIHbgzl-z
func X是否为整数(v any) bool {
	switch v.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
		return true
	}
	return false
}

// X是否为二进制值 函数用于检查给定的字符串是否为有效的二进制值。
// 示例：https://go.dev/play/p/ogPeg2XJH4P
func X是否为二进制值(文本 string) bool {
	return binMatcher.MatchString(文本)
}

// X是否为十六进制值 用于检查给定的字符串是否为有效的十六进制值。
// 示例：https://go.dev/play/p/M2qpHbEwmm7
func X是否为十六进制值(文本 string) bool {
	return hexMatcher.MatchString(文本)
}

// X是否为Base64URL 检查给定的字符串是否是一个有效的 URL 安全的 Base64 编码字符串。
// 在线演示: https://go.dev/play/p/vhl4mr8GZ6S
func X是否为Base64URL(文本 string) bool {
	return base64URLMatcher.MatchString(文本)
}

// X是否为JWT 检查给定的字符串是否是有效的JSON Web Token（JWT）。
// 示例：https://go.dev/play/p/R6Op7heJbKI
func X是否为JWT(文本 string) bool {
	strings := strings.Split(文本, ".")
	if len(strings) != 3 {
		return false
	}

	for _, s := range strings {
		if !X是否为Base64URL(s) {
			return false
		}
	}

	return true
}

// X是否为Visa 检查给定的字符串是否为有效的签证卡号码。
// 示例：https://go.dev/play/p/SdS2keOyJsl
func X是否为Visa(文本 string) bool {
	return visaMatcher.MatchString(文本)
}

// X是否为万事达卡号 检查给定的字符串是否是一个有效的万事达卡号码。
// 在线测试: https://go.dev/play/p/CwWBFRrG27b
func X是否为万事达卡号(文本 string) bool {
	return masterCardMatcher.MatchString(文本)
}

// X是否为运通信用卡号 检查给定的字符串是否为有效的美国运通信用卡号码。
// 示例：https://go.dev/play/p/HIDFpcOdpkd
func X是否为运通信用卡号(文本 string) bool {
	return americanExpressMatcher.MatchString(文本)
}

// X是否为银联卡号 检查给定的字符串是否为有效的银联卡号。
// 示例：https://go.dev/play/p/CUHPEwEITDf
func X是否为银联卡号(文本 string) bool {
	return unionPay.MatchString(文本)
}

// X是否为中国银联卡号 检查给定的字符串是否是一个有效的中国银联号码。
// 在线测试: https://go.dev/play/p/yafpdxLiymu
func X是否为中国银联卡号(文本 string) bool {
	return chinaUnionPay.MatchString(文本)
}
